//
// Created by wchenbt on 3/4/2021.
//
#ifdef ENABLE_MAPLE_SAN

#ifndef MAPLE_SAN_ASAN_STACKVAR_H
#define MAPLE_SAN_ASAN_STACKVAR_H

#include <set>

#include "asan_function.h"
#include "asan_interfaces.h"
#include "me_phase_manager.h"
#include "module_phase_manager.h"
#include "san_common.h"

namespace maple {

class FunctionStackPoisoner {
 public:
  FunctionStackPoisoner(MeFunction &function, AddressSanitizer &asan);

  bool runOnFunction();

  void processStackVariable();

  void unpoisonDynamicAllocas();

  void initializeCallbacks(const MIRModule &M);

  void createDynamicAllocasInitStorage();

  bool isInFirstBlock(StmtNode *stmtNode);

  BaseNode *GetTransformedNode(MIRSymbol *oldVar, MIRSymbol *newVar, BaseNode *baseNode);

  void replaceAllUsesWith(MIRSymbol *oldVar, MIRSymbol *newVar);

  BlockNode *replaceAllUsesOfBlockNode(MIRSymbol *oldVar, MIRSymbol *newVar, BlockNode *block);

  void handleDynamicAllocaCall(ASanDynaVariableDescription *AI);

  MIRSymbol *createAllocaForLayout(StmtNode *insBefore, MIRBuilder *mirBuilder, const ASanStackFrameLayout &L,
                                   MIRSymbol** asan_tmp_ptr);

  void unpoisonDynamicAllocasBeforeInst(StmtBlockNodePair stmtBlockPair);

  void copyToShadow(const std::vector<uint8_t> ShadowMask, const std::vector<uint8_t> ShadowBytes,
                    MIRBuilder *mirBuilder, BaseNode *ShadowBase, StmtNode *InsBefore);

  void copyToShadow(const std::vector<uint8_t> ShadowMask, const std::vector<uint8_t> ShadowBytes, size_t Begin,
                    size_t End, MIRBuilder *mirBuilder, BaseNode *ShadowBase, StmtNode *InsBefore);

  void copyToShadowInline(const std::vector<uint8_t> ShadowMask, const std::vector<uint8_t> ShadowBytes, size_t Begin,
                          size_t End, MIRBuilder *mirBuilder, BaseNode *ShadowBase, StmtNode *InsBefore);

  void CleanStackShadow(const std::vector<uint8_t> ShadowMask, MIRBuilder *mirBuilder, BaseNode *ShadowBase,
                        StmtNode *InsBefore);
  void CleanStackShadowBytes(uint32 bytes, uint64 offset, MIRBuilder *mirBuilder, BaseNode* ShadowBase, StmtNode *InsBefore);

  bool isFuncCallArg(const MIRSymbol *const symbolPtr) const;
  bool isFuncCallArg(const std::string symbolName) const;

  std::set<MIRSymbol *> GetStackVarReferedByCallassigned();

  AddressSanitizer &ASan;

  MeFunction *meFunction;
  MIRFunction *mirFunction;

  MIRModule *module;
  MIRType *IntptrTy;
  MIRType *IntptrPtrTy;
  ShadowMapping Mapping;

  unsigned StackAlignment;
  MIRSymbol *DynamicAllocaLayout = nullptr;

  std::vector<StmtBlockNodePair> RetVec;
  std::vector<ASanStackVariableDescription> stackVariableDesc;
  std::vector<ASanDynaVariableDescription> dynamicAllocaDesc;
  std::set<const MIRSymbol *> callArgSymbols;
  std::set<std::string> callArgSymbolNames;

  MIRFunction *AsanSetShadowFunc[0x100] = {};
  MIRFunction *AsanAllocaPoisonFunc, *AsanAllocasUnpoisonFunc;

  bool HasNonEmptyInlineAsm = false;
  bool HasReturnsTwiceCall = false;

  std::map<MIRSymbol *, bool> isUsedInAlloca;

 private:
  void collectLocalVariablesWithoutAlloca();
  void collectLocalVariablesWithAlloca();
  void collectDescFromUnaryStmtNode(UnaryStmtNode &assignNode);

  BaseNode* TransformAddrofNode(MIRSymbol *oldVar, MIRSymbol *newVar, AddrofNode *baseNode);
  BaseNode* TransformDassignNode(MIRSymbol *oldVar, MIRSymbol *newVar, DassignNode *baseNode);
  BaseNode* TransformDreadNode(MIRSymbol *oldVar, MIRSymbol *newVar, DreadNode *baseNode);
  BaseNode* TransformDassignoffNode(MIRSymbol *oldVar, MIRSymbol *newVar, DassignoffNode *baseNode);

  // handle SSANode, to replace dynamic_cast
  AddrofNode *getAddrofNode(BaseNode *baseNode) {
    SSANode *ssaCheck = dynamic_cast<SSANode *>(baseNode);
    if (ssaCheck != nullptr)
      return dynamic_cast<AddrofNode *>(dynamic_cast<AddrofSSANode *>(baseNode)->GetNoSSANode());
    else
      return dynamic_cast<AddrofNode *>(baseNode);
  }
  DreadNode *getDreadNode(BaseNode *baseNode) {
    SSANode *ssaCheck = dynamic_cast<SSANode *>(baseNode);
    if (ssaCheck != nullptr)
      return dynamic_cast<DreadNode *>(dynamic_cast<SSANode *>(baseNode)->GetNoSSANode());
    else
      return dynamic_cast<DreadNode *>(baseNode);
  }
};
}  // namespace maple
#endif  // MAPLE_SAN_ASAN_STACKVAR_H

#endif